> ## Documentation Index
> Fetch the complete documentation index at: https://sequence-0fb8d9e6-api_docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Modules & wallet update

> This content explains how Sequence wallets are implemented using modules and how the wallet configuration can be updated.

Sequence modules are the underlying program implementation of the wallets; wallets can change modules at runtime.

The `MainModule` is the initial module of every Sequence wallet; it differs from the other modules because it doesn't store the set of signers on contract storage; it uses the [salt](/solutions/technical-references/architecture/v1/wallet-factory) provided to the Factory contract.

## Wallet implementation

Sequence modules can be assigned to wallets either by the factory or by updating it after the initial deployment. Only one module can be assigned to a wallet at a time.

### \_updateConfiguration

The `updateImplementation` allows to update the underlying implementation of the wallet proxy. This implementation contains all the core code that defines the wallet's behaviour.

<Warning>
  Calling `updateImplementation` with an invalid implementation will result in the corruption of the wallet.

  Corrupt wallets may lead to the loss of funds.
</Warning>

```solidity theme={null}
function updateImplementation(
  address _implementation
) external override onlySelf
```

#### Parameters:

| Name             | Type    | Description                               |
| ---------------- | ------- | ----------------------------------------- |
| \_implementation | address | Address of the new wallet implementation. |

This method has the `onlySelf` modifier, which means that it can only be called by the wallet itself using a self-referencing transaction. Calls to this method coming from other addresses, even if these addresses are signers of the wallet, will be rejected.

### Reading current implementation

The wallet implementation is stored on the contract storage slot defined by the address of the wallet itself. Given that every wallet has a unique address, the implementation slot varies from wallet to wallet.

```js theme={null}
import {AbiCoder} "ethers"

const address = "0x596af90cecdbf9a768886e771178fd5561dd27ab"
const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545")

// Read storage slot address(address)
const slot = await provider.getStorageAt(address, AbiCoder.defaultAbiCoder().encode(['address'], [address]))

// Decode bytes32 as address value
const implementation = AbiCoder.defaultAbiCoder().decode(['address'], slot)[0]

console.log(implementation)
```

## Wallet configuration validation

<Note>
  Fixed configuration
  Signer's configuration on wallets using `MainModule` can't be changed. The only way to change the set of signers or threshold is by updating the module of the wallet.
</Note>

All sequence modules must implement the `ModuleAuth` interface, this interface allows the rest of the module to validate signatures for the wallet. In the case of `MainModule` this interface is implemented as a counter-factual validation of hash passed to the factory during the contract wallet creation.

### \_isValidImage

```solidity theme={null}
  function _isValidImage(
    bytes32 _imageHash
  ) internal override view returns (bool _isValid)
```

Validates if the provided `imageHash` corresponds to the one configured in the wallet. This function is called internally to validate transaction and message signatures.

The `imageHash` is a hash of the wallet configuration, which contains the wallet's threshold, signers and weights.

#### Parameters:

| Name        | Type    | Description                                   |
| ----------- | ------- | --------------------------------------------- |
| \_imageHash | bytes32 | Hash of wallet configuration to be validated. |

#### Return Values:

| Name      | Type | Description                                                                    |
| --------- | ---- | ------------------------------------------------------------------------------ |
| \_isValid | bool | True if the given `imageHash` corresponds to the current wallet configuration. |

## MainModuleUpgradeable

`MainModuleUpgradable` is a module that mimics the behaviour of the `MainModule` but allows the wallet configuration to be updated.

### `updateImageHash`

Updates the wallet `imageHash`, this is the hash that defines the wallet configuration (signers, weights, threshold).

```solidity theme={null}
  function updateImageHash(
    bytes32 _imageHash
  ) external override onlySelf
```

#### Parameters:

| Name        | Type    | Description                                   |
| ----------- | ------- | --------------------------------------------- |
| \_imageHash | bytes32 | Hash of the new configuration for the wallet. |

<Warning>
  The `imageHash` is not validated, it is the responsibility of the caller to ensure that the hash is correct. Reasons for incorrect hashes include:

  * The combined weight of the signers is below the threshold.
  * The signers are not valid addresses.
  * The signers are smart contract wallets without proper support for EIP-1271.
  * The `imageHash` doesn't correspond to any wallet configuration (may be a random string).
  * The `imageHash` corresponds to an unknown wallet configuration.

  In any of this cases the wallet will be **rendered unusable**.
</Warning>

This method has the `onlySelf` modifier, which means that it can only be called by the wallet itself using a self-referencing transaction. Calls to this method coming from other addresses, even if these addresses are signers of the wallet, will be rejected.

## First configuration update

When Sequence wallets are created, the factory contract doesn't call an `initialize` function. The configuration is instead defined by the `salt` provided to the factory, the `MainModule` then checks the counterfactual validity of all signatures against the wallet address.

This means there is no direct way to update the configuration of a wallet while still using the `MainModule`. Given that the first configuration update needs to also change the wallet implementation to the `MainModuleUpgradable`, the `MainModule` is updated to the `MainModuleUpgradable` and the `updateImageHash` method is called to update the wallet configuration.

```js theme={null}
const transactions = [
  {
    delegateCall: false,
    revertOnError: true,
    to: wallet,
    data: walletInterface.encodeFunctionData(
      walletInterface.getFunction('updateImplementation'), [this.context.mainModuleUpgradable]
    ),
    value: ethers.constants.Zero,
    gasLimit: ethers.constants.Zero,
  },
  {
    delegateCall: false,
    revertOnError: true,
    to: wallet,
    data: mainModuleInterface.encodeFunctionData(
      mainModuleInterface.getFunction('updateImageHash'), [newImageHash]
    ),
    value: ethers.constants.Zero,
    gasLimit: ethers.constants.Zero,
  }
]
```

#### delegateCall: false

`delegateCall` is used to extend the wallet functionality beyond what's allowed by the module. In this case the called methods are defined on the modules themselves, so there is no need to use `delegateCall`.

#### revertOnError: true

`revertOnError` is used to revert the whole transaction bundle if a transaction flagged by it fails. In this case the operation should be atomic given that a partial wallet configuration update **will** render the wallet unusable.

#### to: wallet

The methods being called are defined on the wallet itself, but need to be called externally, so the `to` address is the wallet itself.

#### value: ethers.constants.Zero

The `value` of the transaction is always zero, since the transaction is a self-referencing transaction and doesn't require transferring funds.

#### gasLimit: ethers.constants.Zero

The `gasLimit` of the transaction is always zero, since it represents an unlimited amount of gas.

<Warning>
  Dangerous operation

  When the wallet is first updated to the `MainModuleUpgradable` it doesn't have a valid `imageHash` yet. It's imperative that the `imageHash` is updated before the transaction bundle finishes executing.
  If the `imageHash` is not updated before the transaction bundle finishes executing, the wallet will be rendered unusable.

  For this reason the following considerations should be taken when updating the wallet for the first time:

  * All transactions should be marked `revertOnError = true`.
  * `updateImplementation` and `updateImageHash` should both be declared on the same transaction bundle.
  * The `gasLimit` of both transactions should be set to unlimited (`0`).
</Warning>

### Subsequent configuration updates

Once the wallet is updated to the `MainModuleUpgradable` it can be updated by calling the `updateImageHash` method, without any additional transaction.

```js theme={null}
const transactions = [
  {
    delegateCall: false,
    revertOnError: true,
    to: wallet,
    data: mainModuleInterface.encodeFunctionData(
      mainModuleInterface.getFunction('updateImageHash'), [newImageHash]
    ),
    value: ethers.constants.Zero,
    gasLimit: ethers.constants.Zero,
  }
]
```

### Retrieving the current configuration

If the wallet is updated to the `MainModuleUpgradable` it can be queried for the current configuration by calling the `getImageHash` method.

This method should return the wallet's current configuration hash, which can be compared to a list of known wallet configurations to find the correct one.

#### Retrieving the wallet configuration

The `imageHash` method returns `bytes32(0)` if the wallet is not yet updated to the `MainModuleUpgradable`.
In this case the wallet is in a counter-factual state and the `imageHash` can't be directly queried.

This is also the case for non-deployed wallets.

To find the `imageHash` of a non-deployed or non-updated wallet, a candidate known `imageHash` needs to be compared against the wallet address.

See [Compute wallet address](/solutions/technical-references/architecture/v1/wallet-configuration).
